/************************************************************************************************************
	TODO: Put usage details here

	Data Organization
	+----------------------------+      +----------------------------+
	| ListData - Column Header 0 | ---> | ListItem - Column Header 1 | ---> etc.
	+----------------------------+      +----------------------------+
	            |
				v
	+----------------------------+      +----------------------------+
	| ListData - Row 0 Column 0  | ---> | ListItem - Row 0 Column 1  | ---> etc.
	+----------------------------+      +----------------------------+
	            |
				v
	+----------------------------+      +----------------------------+
	| ListData - Row 1 Column 0  | ---> | ListItem - Row 1 Column 1  | ---> etc.
	+----------------------------+      +----------------------------+
	            |
				v
			   etc.
	NOTES:	* Column Headers only available to LT_TABLE when LP_COLUMNHEADERS flag is set
			* For LT_TABLE each ListData is a row and a column, and each ListItem is a subsequent column
			* For LT_GRID ListItem is unused; ListData is used for each cell
*/

#include "ui_listwidget.h"
#include "ui_slider.h"
#include "ui_verslider.h"
#include "ui_manager.h"

#include "font/font_manager.h"
#include "graphics/graphics_util.h"
#include "input/mouse.h"
#include "lang/lang.h"
#include "script/scripthelper.h"
#include "script/write_lua.h"


#include "util/timer.h"		    // Needs to come in early
#include "util/mmgr.h"		    // Needs to come in early
#include "util/common_macros.h"		// macros
#include "util/log.h"		    // logging
#include "util/profiler.h"

#include <cassert>
#include <wchar.h>
#include <math.h>

#ifdef _ENABLE_LUABIND
#include <luabind/luabind.hpp>
#endif // _ENABLE_LUABIND

using namespace ui;

UIListWidget::UIListWidget(UIWidget* pParent, const std::string& Name, LIST_TYPE ListType, unsigned long Flags) :
	UIWidget(pParent, Name),
	m_ListType(ListType),
	m_ColumnColor(-1),
	m_DataColor(-1),
	m_ColumnSpacing(5.0f / (float)GraphicsUtil::WIDTH),
	m_RowSpacing(5.0f / (float)GraphicsUtil::HEIGHT),
	m_RowHeight(0.0f),
	m_Properties(Flags),
	m_Width(1),
	m_Height(0),
	m_CurrentRow(0),
	m_CurrentCol(0),
	m_pHover(NULL),
	m_pSliderH(NULL),
	m_pSliderV(NULL),
	m_pSortFunc(NULL),
	m_pSelectFunc(NULL),
	m_pPreRenderFunc(NULL),
	m_pPostRenderFunc(NULL),
	m_pEditFunc(NULL),
	m_pvSortCBData(NULL),
	m_pvSelectCBData(NULL),
	m_pvPreRenderCBData(NULL),
	m_pvPostRenderCBData(NULL),
	m_pvEditCBData(NULL),
	m_CacheDirty(true),
	m_SliderCacheH(-1.0f),
	m_SliderCacheV(-1.0f),
	m_pSelectFuncDg(0),
	m_pPreRenderFuncDg(0),
	m_pPostRenderFuncDg(0),
	m_pEditFuncDg(0)
{
	m_Type = WT_LIST;
	m_ColumnFont = m_DataFont = GetFont();
	SetSortCB(UIListWidget::DefaultSortFuncCB);

	// default first column data
	COLUMN_SPECIFICS col;
	m_ColumnSpecs.push_back(col);
}

UIListWidget::~UIListWidget()
{
	for (int i = 0; i < (int)m_ListItems.size(); i++)
		delete m_ListItems[i];
	m_ListItems.clear();
}

bool UIListWidget::Create(const std::string& Name, const math::Vec2& Pos, const math::Vec2& Size)
{
	if (UIWidget::Create(Name, Pos, Size))
	{
		ClearList();
		RecreateList();
		return true;
	}
	return false;
}

bool UIListWidget::Load(LuaPlus::LuaObject& _LObj)
{
	using namespace script;

	UIWidget::Load(_LObj);

	// should clear and reinit data? or assume Load will only be called once?
	m_Properties = 0;

	std::string szData;
	int iData;
	bool bData;
	math::Vec4 v4Data;

	szData				= ScriptHelper::GetTableString(_LObj, "ListType", "table");
	m_ListType			= MapStringToList(szData);

	// load generics
	m_RowSpacing		= ScriptHelper::GetTableFloat(_LObj, "RowSpacing", 5.0f/GraphicsUtil::WIDTH);
	m_ColumnSpacing		= ScriptHelper::GetTableFloat(_LObj, "ColumnSpacing", 5.0f/GraphicsUtil::HEIGHT);
	m_DataFont			= ScriptHelper::GetTableString(_LObj, "DataFont", GetFont());

	v4Data				= ScriptHelper::GetTableVec4(_LObj, "DataColor", math::Vec4(1.0f, 1.0f, 1.0f, 1.0f));
	m_DataColor			= ColorfRGBA(v4Data.x, v4Data.y, v4Data.z, v4Data.w);

	bData				= ScriptHelper::GetTableBool(_LObj, "CanScrollVert", true);
	if (bData)			m_Properties |= LP_CANVSCROLL;
	bData				= ScriptHelper::GetTableBool(_LObj, "CanSort", true);
	if (bData)			m_Properties |= LP_CANSORT;
	bData				= ScriptHelper::GetTableBool(_LObj, "AutoRowHeight", true);
	if (!bData)			m_Properties |= LP_MANUALROWSIZE;
	bData				= ScriptHelper::GetTableBool(_LObj, "EnableMultiSelect", false);
	if (bData)			m_Properties |= LP_MULTISELECT;

	if (IsTable())
	{
		// load table specifics
		m_ColumnFont	= ScriptHelper::GetTableString(_LObj, "ColumnFont", GetFont());

		v4Data			= ScriptHelper::GetTableVec4(_LObj, "ColumnColor", math::Vec4(1.0f, 1.0f, 1.0f, 1.0f));
		m_ColumnColor	= ColorfRGBA(v4Data.x, v4Data.y, v4Data.z, v4Data.w);

		bData			= ScriptHelper::GetTableBool(_LObj, "ColumnHeaders", false);
		if (bData)		m_Properties |= LP_COLUMNHEADERS;
		bData			= ScriptHelper::GetTableBool(_LObj, "CanScrollHorz", false);
		if (bData)		m_Properties |= LP_CANHSCROLL;
		bData			= ScriptHelper::GetTableBool(_LObj, "AutoColumnWidth", true);
		if (!bData)		m_Properties |= LP_MANUALCOLUMNSIZE;
		bData			= ScriptHelper::GetTableBool(_LObj, "VariableHeight", false);
		if (bData)		m_Properties |= LP_VARIABLE_HEIGHT;

		iData			= ScriptHelper::GetTableInt(_LObj, "ColumnC", 1);

		for (int i = 1; i < iData; i++)
			AddColumnW(L"");
	}
	else
	{
		// load grid specifics
		bData			= ScriptHelper::GetTableBool(_LObj, "DisableText", false);
		if (bData)		m_Properties |= LP_DISABLETEXT;
	}

	// misc loading prepwork
	if (m_Properties & LP_CANVSCROLL)
	{
		szData			= ScriptHelper::GetTableString(_LObj, "VertScrollBar", "");
		if (!szData.empty())
		{
			m_DelayedLoad.HasData = true;
			m_DelayedLoad.LoadVSlider = szData;
		}
	}

	if (m_Properties & LP_CANHSCROLL)
	{
		szData			= ScriptHelper::GetTableString(_LObj, "HorzScrollBar", "");
		if (!szData.empty())
		{
			m_DelayedLoad.HasData = true;
			m_DelayedLoad.LoadHSlider = szData;
		}
	}

	if (m_Properties & LP_MANUALROWSIZE)
		m_RowHeight		= ScriptHelper::GetTableFloat(_LObj, "RowHeight", 0.0f);

	if (IsTable())
	{
		for (int i = 0; i < (int)m_ColumnSpecs.size(); i++)
		{
			if (m_Properties & LP_MANUALCOLUMNSIZE)
				m_ColumnSpecs[i].Width	= ScriptHelper::GetTableFloat(_LObj, "ColumnWidth" + lang::Lang::FormatNumber(i+1), 0.0f);

			szData		= ScriptHelper::GetTableString(_LObj, "ColumnAlignH" + lang::Lang::FormatNumber(i+1), "");
			if (!szData.empty())
				m_ColumnSpecs[i].DefaultHJust = UIText::MapStringToHorizJust(szData);

			szData		= ScriptHelper::GetTableString(_LObj, "ColumnAlignV" + lang::Lang::FormatNumber(i+1), "");
			if (!szData.empty())
				m_ColumnSpecs[i].DefaultVJust = UIText::MapStringToVertJust(szData);
		}
	}

	RecreateList(true);

	if (IsTable() && (m_Properties & LP_COLUMNHEADERS))
	{
		iData = GetColumnC();
		for (int i = 0; i < iData; i++)
		{
			szData		= ScriptHelper::GetTableString(_LObj, "ColumnTitle" + lang::Lang::FormatNumber(i+1), "");
			if (!szData.empty())
				SetColumnTextA(szData, i);
		}
	}

	return true;
}

bool UIListWidget::Tick(float dt)
{
	PROFILE_BLOCK("UIListWidget_Tick");

	if (m_DelayedLoad.HasData)
	{
		// comes in before everyone else
		ui::UIWidget* pParent = this;
		while (pParent->GetParent()) // get top level parent
			pParent = pParent->GetParent();

		if (pParent && !m_DelayedLoad.LoadHSlider.empty() && (m_Properties & LP_CANHSCROLL))
			SetSliderH( pParent->GetSliderByName(m_DelayedLoad.LoadHSlider) );
			//SetSliderH( (UISlider*)UIManager::Instance().GetWidget(m_DelayedLoad.LoadHSlider) );

		if (pParent && !m_DelayedLoad.LoadVSlider.empty() && (m_Properties & LP_CANVSCROLL))
		{
			UIWidget* pVerSlider = pParent->GetWidgetByName(m_DelayedLoad.LoadVSlider);
			if (pVerSlider)
			{
				if (WT_VERSLIDER == pVerSlider->GetType())
					SetSliderV( (UIVerSlider*)pVerSlider );
			}
		}
			//SetSliderV( (UIVerSlider*)UIManager::Instance().GetWidget(m_DelayedLoad.LoadVSlider) );

		ResetSliders();
		m_DelayedLoad.HasData = false;
	}

	if (UIWidget::Tick(dt))
	{
		for (int i = 0; i < (int)m_ListItems.size(); i++)
		{
			ListData* pData = m_ListItems[i];

			pData->m_Sprite.Tick(dt);

			if (IsTable())
			{
				for (int j = 0; j < (int)pData->m_ColumnData.size(); j++)
				{
					ListItem& item = pData->m_ColumnData[j];

					item.m_Sprite.Tick(dt);
				}
			}
		}

		// since we can't have the sliders callback to us, we poll its value every interval
		if (m_pSliderV && (m_Properties & LP_CANVSCROLL))
		{
			float value = m_pSliderV->GetSlider();
			if (value != m_SliderCacheV)
			{
				int RowC = IsGrid() ? GetRowC() : GetItemC();
				int MaxRows = GetMaxVisibleRows(m_CurrentRow);
				int RowIdx = RowC - MaxRows;

				if (IsTable() && (m_Properties & LP_COLUMNHEADERS))
					RowIdx++;

				if (m_SliderCacheV < 0.0f)
				{
					// initialize slider to current scroll
					if (RowIdx > 0)
						value = (float)m_CurrentRow / RowIdx;
					else
						value = 0.0f;
					m_pSliderV->SetSlider(value);
				}
				else
				{
					// set current scroll to slider
					if (IsGrid())
						SetVScroll(GetColumnC() * (int)floor(0.5f + (value*RowIdx)), true);
					else
						SetVScroll((int)floor(0.5f + (value*RowIdx)), true);
				}
			}
			m_SliderCacheV = value;
		}

		if (m_pSliderH && (m_Properties & LP_CANHSCROLL) && IsTable())
		{
			float value = m_pSliderH->GetSlider();
			if (m_SliderCacheH != value)
			{
				int ColumnC = GetColumnC();
				int MaxCols = GetMaxVisibleCols(m_CurrentCol);
				int ColIdx = ColumnC - MaxCols;

				if (m_SliderCacheH < 0.0f)
				{
					// initialize slider to current scroll
					if (ColIdx > 0)
						value = (float)m_CurrentCol / ColIdx;
					else
						value = 0.0f;
					m_pSliderH->SetSlider(value);
				}
				else
				{
					// set current scroll to slider
					SetHScroll((int)floor(0.5f + (value*ColIdx)), true);
				}
			}
			m_SliderCacheH = value;
		}

		return true;
	}
	return false;
}

bool UIListWidget::Render(bool Force)
{
	PROFILE_BLOCK("UIListWidget_Render");

	if (UIWidget::Render(Force))
	{
		math::Vec2 From = GetPosition();
		math::Vec2 To = From + GetSize();
		math::Vec2 Current = From;
		math::Vec2 ItemSize(-1, -1);

		if (m_CacheDirty)
		{
			RecreateList();
		}

		ItemSize.y = m_RowHeight;
		m_CurrentRow = UTIL_LOW(m_CurrentRow, (int)m_ListItems.size());

		for (int i = m_CurrentRow; i < (int)m_ListItems.size(); i++)
		{
			ListData* pData = NULL;
			bool AddColumnW = true;
			int idx;

			if (IsTable() && (m_Properties & LP_COLUMNHEADERS) && (m_CurrentRow > 0))
			{
				if (i == m_CurrentRow)
				{
					// always render column header regardless of scroll
					idx = 0;
				}
				else
					idx = i-1;
			}
			else
				idx = i;

			pData = m_ListItems[idx];

			ItemSize.x = pData->m_Width;
			if (IsTable() && (m_Properties & LP_VARIABLE_HEIGHT))
				ItemSize.y = pData->m_Height;

			if (Current.y > (To.y - ItemSize.y)) // do we want to perform partial rendering? can fonts/text support it?
				break;

			m_CurrentCol = UTIL_LOW(m_CurrentCol, (int)pData->m_ColumnData.size());

			if (m_pPreRenderFunc)
			{
				if (IsTable())
					m_pPreRenderFunc(pData, Current, math::Vec2(To.x, Current.y + ItemSize.y), this, m_pvPreRenderCBData);
				else
					m_pPreRenderFunc(pData, Current, ItemSize, this, m_pvPreRenderCBData);
			}
			if (m_pPreRenderFuncDg)
			{
				if (IsTable())
					m_pPreRenderFuncDg(pData, Current, math::Vec2(To.x, Current.y + ItemSize.y), this);
				else
					m_pPreRenderFuncDg(pData, Current, ItemSize, this);
			}


			if (IsTable() || (m_CurrentCol == 0))
			{
				RenderItem(pData, Current, ItemSize, idx, m_CurrentCol);
			}
			else
				AddColumnW = false;

			// next item
			if (IsGrid())
			{
				if (m_pPostRenderFunc)
					m_pPostRenderFunc(pData, Current, Current+ItemSize, this, m_pvPostRenderCBData);
				if (m_pPostRenderFuncDg)
					m_pPostRenderFuncDg(pData, Current, Current+ItemSize, this);

				Current.x += ItemSize.x + m_ColumnSpacing;
				if (Current.x > (To.x - ItemSize.x))
				{
					Current.x = From.x;
					Current.y += ItemSize.y + m_RowSpacing;
					//if (Current.y > (To.y - ItemSize.y))
					//	break;
				}
			}
			else
			{
				// render rest of this row's columns
				for (int j = 0; j < (int)pData->m_ColumnData.size(); j++)
				{
					ListItem& item = pData->m_ColumnData[j];

					if (AddColumnW)
						Current.x += ItemSize.x + m_ColumnSpacing;
					else
						AddColumnW = true;

					ItemSize.x = item.m_Width;
					if (Current.x > (To.x - ItemSize.x)) // do we want to perform partial rendering? can fonts/text support it?
						break;

					RenderItem(&item, Current, ItemSize, idx, j+1);
				}

				if (m_pPostRenderFunc)
					m_pPostRenderFunc(pData, math::Vec2(From.x, Current.y), math::Vec2(To.x, Current.y + ItemSize.y), this, m_pvPostRenderCBData);
				if (m_pPostRenderFuncDg)
					m_pPostRenderFuncDg(pData, math::Vec2(From.x, Current.y), math::Vec2(To.x, Current.y + ItemSize.y), this);

				// next row
				Current.x = From.x;
				Current.y += ItemSize.y + m_RowSpacing;
				//if (Current.y > (To.y - ItemSize.y))
				//	break;
			}
		}
		return true;
	}
	return false;
}

bool UIListWidget::OnMouseMove(const math::Vec2& From, const math::Vec2& To)
{
	bool bRet = UIWidget::OnMouseMove(From, To);
	ListItem* pItem = GetItem(To);
	ListData* pParent = pItem ? pItem->GetParent() : NULL;
	if (m_pHover != pParent)
	{
		m_pHover = pParent;
	}
	return bRet;
}

bool UIListWidget::OnMousePress(const math::Vec2& Pos, unsigned int Button)
{
	bool bRet = UIWidget::OnMousePress(Pos, Button);
	if (Button == input::Mouse::BUTTON_LEFT)
	{
		if (ListItem* pItem = GetItem(Pos))
			m_pHover = pItem->GetParent();
		else
			m_pHover = NULL;
	}
	return bRet;
}

bool UIListWidget::OnMouseDblClick(const math::Vec2& Pos, unsigned int Button)
{
	bool bRet = UIWidget::OnMouseDblClick(Pos, Button);
	if (m_Properties & LP_CANEDIT)
	{
		if (ListItem* pItem = GetItem(Pos))
		{
			// TODO: edit text on doubleclick
		}
	}
	return bRet;
}

bool UIListWidget::OnMouseRelease(const math::Vec2& Pos, unsigned int Button)
{
	bool bRet = UIWidget::OnMouseRelease(Pos, Button);
	if (ListItem* pItem = GetItem(Pos))
	{
		if (!(m_Properties & LP_MULTISELECT))
			ClearAllSelectedItems();

		if (pItem->GetParent()->IsSelected())
			pItem->GetParent()->m_SelectedIdx = -1; // set unselected
		else
			pItem->GetParent()->m_SelectedIdx = 0; // set selected

		if (m_pSelectFunc)
			m_pSelectFunc(pItem, pItem->GetParent()->IsSelected(), this, m_pvSelectCBData);
		if (m_pSelectFuncDg)
		{
			m_pSelectFuncDg(pItem, pItem->GetParent()->IsSelected(), this);
		}
	}
	return bRet;
}

bool UIListWidget::OnKeyEvent(const input::Keyboard::KEY& Key)
{
	bool bRet = UIWidget::OnKeyEvent(Key);
	// TODO: on key pressed
	return bRet;
}

void UIListWidget::RefreshText()
{
	UIWidget::RefreshText();

	for (int i = 0; i < (int)m_ListItems.size(); i++)
	{
		ListData* pData = m_ListItems[i];
		pData->m_TextObj.RefreshText();
		for (int j = 0; j < (int)pData->m_ColumnData.size(); j++)
		{
			ListItem& item = pData->m_ColumnData[j];
			item.m_TextObj.RefreshText();
		}
	}

	// do we need to rebuild the table for this?
	// m_CacheDirty = true;
}

UIWidget* UIListWidget::GetClone()
{
	UIListWidget* pList = new UIListWidget(m_pParent, GetCloneName(m_Name), m_ListType, m_Properties);
	FillCloneInfo( (UIWidget*)pList );

	pList->m_CacheDirty			= true;
	pList->m_ColumnFont			= m_ColumnFont;
	pList->m_ColumnSpacing		= m_ColumnSpacing;
	pList->m_CurrentCol			= m_CurrentCol;
	pList->m_CurrentRow			= m_CurrentRow;
	pList->m_DataFont			= m_DataFont;
	pList->m_DelayedLoad		= m_DelayedLoad;
	pList->m_Height				= m_Height;
	pList->m_pEditFunc			= m_pEditFunc;
	pList->m_pPostRenderFunc	= m_pPostRenderFunc;
	pList->m_pPreRenderFunc		= m_pPreRenderFunc;
	pList->m_pSelectFunc		= m_pSelectFunc;
	pList->m_pEditFuncDg		= m_pEditFuncDg;
	pList->m_pPostRenderFuncDg	= m_pPostRenderFuncDg;
	pList->m_pPreRenderFuncDg	= m_pPreRenderFuncDg;
	pList->m_pSelectFuncDg		= m_pSelectFuncDg;

	pList->m_pSliderH			= m_pSliderH;
	pList->m_pSliderV			= m_pSliderV;
	pList->m_pSortFunc			= m_pSortFunc;
	pList->m_pvEditCBData		= m_pvEditCBData;
	pList->m_pvPostRenderCBData	= m_pvPostRenderCBData;
	pList->m_pvPreRenderCBData	= m_pvPreRenderCBData;
	pList->m_pvSelectCBData		= m_pvSelectCBData;
	pList->m_pvSortCBData		= m_pvSortCBData;
	pList->m_RowHeight			= m_RowHeight;
	pList->m_RowSpacing			= m_RowSpacing;
	pList->m_SliderCacheH		= m_SliderCacheH;
	pList->m_SliderCacheV		= m_SliderCacheV;
	pList->m_Width				= m_Width;

	pList->m_ColumnSpecs.clear();
	for (int i = 0; i < (int)m_ColumnSpecs.size(); i++)
	{
		COLUMN_SPECIFICS& col = m_ColumnSpecs[i];
		COLUMN_SPECIFICS newcol = col;
		pList->m_ColumnSpecs.push_back(newcol);
	}

	pList->ClearList();
	for (int i = 0; i < (int)m_ListItems.size(); i++)
	{
		ListData* pData = m_ListItems[i];
		ListData* pNewData = new ListData();
		
		pNewData->m_CustomHJust		= pData->m_CustomHJust;
		pNewData->m_CustomVJust		= pData->m_CustomVJust;
		pNewData->m_Data			= pData->m_Data;
		pNewData->m_Icon			= pData->m_Icon;
		pNewData->m_IsColumnHeader	= pData->m_IsColumnHeader;
		pNewData->m_Text			= pData->m_Text;

		pList->InitTextObj(pNewData, 0);
		pNewData->m_TextObj.SetOwner(pList);
		if (!pNewData->m_Text.empty())
			pNewData->m_TextObj.SetText(pNewData->m_Text);

		if (!pNewData->m_Icon.empty())
		{
			pNewData->m_Sprite.Create(pNewData->m_Icon);
			pNewData->m_Sprite.m_Size	= pData->m_Sprite.m_Size;
		}

		for (int j = 0; j < (int)pData->m_ColumnData.size(); j++)
		{
			ListItem& item = pData->m_ColumnData[j];
			ListItem newitem(pNewData);

			newitem.m_CustomHJust	= item.m_CustomHJust;
			newitem.m_CustomVJust	= item.m_CustomVJust;
			newitem.m_Data			= item.m_Data;
			newitem.m_Icon			= item.m_Icon;
			newitem.m_Text			= item.m_Text;

			pList->InitTextObj(&newitem, j+1);
			newitem.m_TextObj.SetOwner(pList);
			if (!newitem.m_Text.empty())
				newitem.m_TextObj.SetText(newitem.m_Text);

			if (!newitem.m_Icon.empty())
			{
				newitem.m_Sprite.Create(newitem.m_Icon);
				newitem.m_Sprite.m_Size	= item.m_Sprite.m_Size;
			}
		}

		pList->m_ListItems.push_back(pNewData);
	}

	pList->RecreateList(true);
	return pList;
}

void UIListWidget::UpdateLocalization()
{
	UIWidget::UpdateLocalization();

	for (int i = 0; i < (int)m_ListItems.size(); i++)
	{
		ListData* pData = m_ListItems[i];
		pData->m_Sprite.UpdateLocalization();

		for (int j = 0; j < (int)pData->m_ColumnData.size(); j++)
		{
			ListItem& item = pData->m_ColumnData[j];
			item.m_Sprite.UpdateLocalization();
		}
	}

	m_CacheDirty = true;
}

void UIListWidget::RefreshFonts()
{
	UIWidget::RefreshFonts();

	for (int i = 0; i < (int)m_ListItems.size(); i++)
	{
		ListData* pData = m_ListItems[i];
		pData->m_TextObj.SetFont(pData->m_TextObj.GetFontName());
		for (int j = 0; j < (int)pData->m_ColumnData.size(); j++)
		{
			ListItem& item = pData->m_ColumnData[j];
			item.m_TextObj.SetFont(item.m_TextObj.GetFontName());
		}
	}

	m_CacheDirty = true;
}

void UIListWidget::SetColumnFont(const std::string& Font)
{
	m_ColumnFont = Font;
	if (IsTable() && (m_Properties & LP_COLUMNHEADERS) && !m_ListItems.empty() && m_ListItems[0]->m_IsColumnHeader)
	{
		ListData* pData = m_ListItems[0];
		InitTextObj(pData, 0);
		for (int i = 0; i < (int)pData->m_ColumnData.size(); i++)
		{
			ListItem& item = pData->m_ColumnData[i];
			InitTextObj(&item, i+1);
		}
	}

	m_CacheDirty = true;
}

void UIListWidget::SetDataFont(const std::string& Font)
{
	m_DataFont = Font;
	for (int i = 0; i < (int)m_ListItems.size(); i++)
	{
		ListData* pData = m_ListItems[i];
		if (pData->m_IsColumnHeader) continue;

		InitTextObj(pData, 0);
		for (int j = 0; j < (int)pData->m_ColumnData.size(); j++)
		{
			ListItem& item = pData->m_ColumnData[j];
			InitTextObj(&item, j+1);
		}
	}

	m_CacheDirty = true;
}

void UIListWidget::SetColumnColor(COLOR c, bool ForceReset)
{
	if (!ForceReset && (c == m_ColumnColor)) return;

	const COLOR OldColor = m_ColumnColor;
	m_ColumnColor = c;

	if ((m_Properties & LP_COLUMNHEADERS) && !m_ListItems.empty())
	{
		ListData* pData = m_ListItems[0];
		if (pData->m_IsColumnHeader)
		{
			if (ForceReset || (OldColor == pData->m_TextColor))
				pData->m_TextColor = c;

			for (int i = 0; i < (int)pData->m_ColumnData.size(); i++)
			{
				ListItem& item = pData->m_ColumnData[i];
				if (ForceReset || (OldColor == item.m_TextColor))
					item.m_TextColor = c;
			}
		}
	}
}

void UIListWidget::SetDataColor(COLOR c, bool ForceReset)
{
	if (!ForceReset && (c == m_DataColor)) return;

	const COLOR OldColor = m_DataColor;
	m_DataColor = c;

	for (int i = 0; i < (int)m_ListItems.size(); i++)
	{
		ListData* pData = m_ListItems[i];
		if (pData->m_IsColumnHeader) continue;

		if (ForceReset || (OldColor == pData->m_TextColor))
			pData->m_TextColor = c;

		for (int j = 0; j < (int)pData->m_ColumnData.size(); j++)
		{
			ListItem& item = pData->m_ColumnData[j];
			if (ForceReset || (OldColor == item.m_TextColor))
				item.m_TextColor = c;
		}
	}
}

void UIListWidget::SetAlignment(UIText::TEXT_JUST_HORIZ HJust, UIText::TEXT_JUST_VERT VJust, int Column)
{
	if (IsGrid()) return;
	if (Column >= (int)m_Width) return;

	m_ColumnSpecs[Column].DefaultHJust = HJust;
	m_ColumnSpecs[Column].DefaultVJust = VJust;

	for (int i = 0; i < (int)m_ListItems.size(); i++)
	{
		ListData* pData = m_ListItems[i];
		InitTextObj(pData, 0);

		for (int j = 0; j < (int)pData->m_ColumnData.size(); j++)
		{
			ListItem& item = pData->m_ColumnData[j];
			InitTextObj(&item, j+1);
		}
	}

	m_CacheDirty = true;
}

void UIListWidget::SetCellAlignment(UIText::TEXT_JUST_HORIZ HJust, UIText::TEXT_JUST_VERT VJust, int Row, int Column)
{
	if (IsGrid()) return;
	RecreateList();

	if (ListItem* pItem = GetItem(Row, Column, true))
	{
		pItem->m_CustomHJust = (int)HJust;
		pItem->m_CustomVJust = (int)VJust;
		InitTextObj(pItem, Column);

		m_CacheDirty = true;
	}
}

void UIListWidget::SetItemTextA(const std::string& Text, int Row, int Column)
{
	SetItemTextW( lang::Lang::ConvertAtoW(Text), Row, Column );
}

void UIListWidget::SetItemTextW(const std::wstring& Text, int Row, int Column)
{
	if (ListItem* pItem = GetItem(Row, Column))
	{
		math::Vec2 CellSize;
		CellSize.x = pItem->m_Width;
		if (m_Properties & LP_VARIABLE_HEIGHT)
			CellSize.y = pItem->GetParent()->m_Height;
		else
			CellSize.y = m_RowHeight;

		pItem->m_Text = Text;
		pItem->m_TextObj.SetOwner(this);
		pItem->m_TextObj.SetExtents(pItem->m_TextObj.GetPos(), CellSize);
		pItem->m_TextObj.SetText(Text);
	}
}

std::string UIListWidget::GetItemTextA(int Row, int Column)
{
	return lang::Lang::ConvertWtoA( GetItemTextW(Row, Column) );
}

std::wstring UIListWidget::GetItemTextW(int Row, int Column)
{
	if (ListItem* pItem = GetItem(Row, Column))
		return pItem->m_Text;
	return L"";
}

void UIListWidget::SetItemIcon(const std::string& Icon, const math::Vec2& Size, int Row, int Column)
{
	if (ListItem* pItem = GetItem(Row, Column))
	{
		pItem->m_Icon = Icon;
		if (Icon.empty())
			pItem->m_Sprite.Invalidate();
		else
		{
			pItem->m_Sprite.Create(Icon);
			if((Size.x > 0.0f) && (Size.y > 0.0f))
				pItem->m_Sprite.m_Size = Size;
		}
	}
	m_CacheDirty = true;
}

std::string UIListWidget::GetItemIcon(int Row, int Column)
{
	if (ListItem* pItem = GetItem(Row, Column))
		return pItem->m_Icon;
	return "";
}

UISprite* UIListWidget::GetItemSprite(int Row, int Column)
{
	if (ListItem* pItem = GetItem(Row, Column))
		return &pItem->m_Sprite;
	return NULL;
}

void UIListWidget::SetItemData(unsigned long Data, int Row, int Column)
{
	if (ListItem* pItem = GetItem(Row, Column))
		pItem->m_Data.dwData = Data;
}

unsigned long UIListWidget::GetItemData(int Row, int Column)
{
	if (ListItem* pItem = GetItem(Row, Column))
		return pItem->m_Data.dwData;
	return 0;
}

void UIListWidget::SetItemPointer(void* pvData, int Row, int Column)
{
	if (ListItem* pItem = GetItem(Row, Column))
		pItem->m_Data.pvData = pvData;
}

void* UIListWidget::GetItemPointer(int Row, int Column)
{
	if (ListItem* pItem = GetItem(Row, Column))
		return pItem->m_Data.pvData;
	return NULL;
}

void UIListWidget::SetItemColor(COLOR c, int Row, int Column, bool AllowGetHeader)
{
	if (ListItem* pItem = GetItem(Row, Column, AllowGetHeader))
		pItem->m_TextColor = c;
}

COLOR UIListWidget::GetItemColor(int Row, int Column, bool AllowGetHeader)
{
	if (ListItem* pItem = GetItem(Row, Column, AllowGetHeader))
		return pItem->m_TextColor;
	return -1;
}

void UIListWidget::SetRowColor(COLOR c, int Row, bool AllowGetHeader)
{
	for (int i = 0; i < (int)m_ColumnSpecs.size(); i++)
		SetItemColor(c, Row, i, AllowGetHeader);
}

ListItem* UIListWidget::GetItem(int Row, int Column, bool AllowGetHeader)
{
	if (IsGrid())
	{
		if (Column < 0)
		{
			if (Row < (int)m_ListItems.size())
				return m_ListItems[Row];
		}
		else
		{
			// treat as indexed data
			int idx = Row + (m_Width * Column);
			if (idx < (int)m_ListItems.size())
				return m_ListItems[idx];
		}
	}
	else
	{
		if (!AllowGetHeader && (m_Properties & LP_COLUMNHEADERS))
			Row++;

		if (Row <(int)m_ListItems.size())
		{
			if ((Column > 0) && (Column <= (int)m_ListItems[Row]->m_ColumnData.size()))
				return &m_ListItems[Row]->m_ColumnData[Column-1];
			else
				return m_ListItems[Row];
		}
	}
	return NULL;
}

ListItem* UIListWidget::GetItem(const math::Vec2& Pos, bool AllowGetHeader)
{
	math::Vec2 From = GetPosition();
	math::Vec2 To = From + GetSize();
	math::Vec2 Current = From;
	math::Vec2 CellFrom, CellTo;
	float Height = m_RowHeight;

	if (!UTIL_RANGE(Pos.x, From.x, To.x) || !UTIL_RANGE(Pos.y, From.y, To.y))
		return NULL;

	RecreateList();

	if (IsTable())
	{
		int row = 0;
		CellFrom.y = From.y;
		while (row >= 0)
		{
			if ((row + m_CurrentRow) >= (int)m_ListItems.size())
				return NULL;

			if (m_Properties & LP_VARIABLE_HEIGHT)
			{
				if ((m_Properties & LP_COLUMNHEADERS) && (m_CurrentRow > 0))
				{
					if (row == 0) // first row is always column header regardless of scrolling
						Height = m_ListItems[0]->m_Height;
					else
						Height = m_ListItems[row + m_CurrentRow - 1]->m_Height;
				}
				else
					Height = m_ListItems[row + m_CurrentRow]->m_Height;
			}

			CellTo.y = CellFrom.y + Height;

			if (UTIL_RANGE(Pos.y, CellFrom.y, CellTo.y))
			{
				ListData* pData = NULL;
				
				if ((m_Properties & LP_COLUMNHEADERS) && (m_CurrentRow > 0))
				{
					if (row == 0) // first row is always column header regardless of scrolling
						return AllowGetHeader ? m_ListItems[0] : NULL;
					else
						pData = m_ListItems[row + m_CurrentRow - 1];
				}
				else
					pData = m_ListItems[row + m_CurrentRow];

				if (pData && pData->m_IsColumnHeader)
					return AllowGetHeader ? pData : NULL;
				else
					return pData;
			}

			if (CellTo.y >= To.y)
				return NULL;

			row++;
			CellFrom.y += Height + m_RowSpacing;
		}
	}
	else
	{
		for (int y = 0; y < (int)m_Height; y++)
		for (int x = 0; x < (int)m_Width; x++)
		{
			int idx = m_CurrentRow + x + (y * m_Width);
			if (idx >= (int)m_ListItems.size()) return NULL;

			ListData* pData = m_ListItems[idx];
			CellFrom.x = From.x + (float(x) * (pData->m_Width + m_ColumnSpacing));
			CellFrom.y = From.y + (float(y) * (Height + m_RowSpacing));
			CellTo.x = CellFrom.x + pData->m_Width;
			CellTo.y = CellFrom.y + Height;

			if (UTIL_RANGE(Pos.x, CellFrom.x, CellTo.x) && UTIL_RANGE(Pos.y, CellFrom.y, CellTo.y))
			{
				if (!AllowGetHeader && pData->m_IsColumnHeader)
					return NULL;
				else
					return pData;
			}

			if (CellTo.y >= To.y)
				return NULL;
		}
	}

	return NULL;
}

bool UIListWidget::FindItem(const std::string& Text, int* pRow, int* pCol)
{
	return FindItem( lang::Lang::ConvertAtoW(Text), pRow, pCol );
}

bool UIListWidget::FindItem(const std::wstring& Text, int* pRow,int* pCol)
{
	bool ColHeader = false;
	for (int i = 0; i < (int)m_ListItems.size(); i++)
	{
		ListData* pData = m_ListItems[i];

		if (pData->m_IsColumnHeader)
		{
			ColHeader = true;
			continue;
		}

		if (wcscmp(Text.c_str(), pData->m_Text.c_str()) == 0)
		{
			if (pRow) *pRow = ColHeader ? (i-1) : i;
			if (pCol) *pCol = 0;
			return true;
		}

		for (int j = 0; j < (int)pData->m_ColumnData.size(); j++)
		{
			ListItem& item = pData->m_ColumnData[j];
			if (wcscmp(Text.c_str(), item.m_Text.c_str()) == 0)
			{
				if (pRow) *pRow = ColHeader ? (i-1) : i;
				if (pCol) *pCol = j+1;
				return true;
			}
		}
	}

	return false;
}

bool UIListWidget::FindItem(unsigned long Data, int* pRow, int* pCol)
{
	bool ColHeader = false;
	for (int i = 0; i < (int)m_ListItems.size(); i++)
	{
		ListData* pData = m_ListItems[i];

		if (pData->m_IsColumnHeader)
		{
			ColHeader = true;
			continue;
		}

		if (Data == pData->m_Data.dwData)
		{
			if (pRow) *pRow = ColHeader ? (i-1) : i;
			if (pCol) *pCol = 0;
			return true;
		}

		for (int j = 0; j < (int)pData->m_ColumnData.size(); j++)
		{
			ListItem& item = pData->m_ColumnData[j];
			if (Data == pData->m_Data.dwData)
			{
				if (pRow) *pRow = ColHeader ? (i-1) : i;
				if (pCol) *pCol = j+1;
				return true;
			}
		}
	}

	return false;
}

bool UIListWidget::FindItem(void* pvData, int* pRow, int* pCol)
{
	bool ColHeader = false;
	for (int i = 0; i < (int)m_ListItems.size(); i++)
	{
		ListData* pData = m_ListItems[i];

		if (pData->m_IsColumnHeader)
		{
			ColHeader = true;
			continue;
		}

		if (pvData == pData->m_Data.pvData)
		{
			if (pRow) *pRow = ColHeader ? (i-1) : i;
			if (pCol) *pCol = 0;
			return true;
		}

		for (int j = 0; j < (int)pData->m_ColumnData.size(); j++)
		{
			ListItem& item = pData->m_ColumnData[j];
			// Commented out by Pax
			// Cause finding item doesn't work
			// Replaced with Sleipner 1's code for this line
			//if (pvData == pData->m_Data.pvData)
			if (pvData == item.m_Data.pvData)
			{
				if (pRow) *pRow = ColHeader ? (i-1) : i;
				if (pCol) *pCol = j+1;
				return true;
			}
		}
	}

	return false;
}

void UIListWidget::SetItemSelected(int Row, int Column, bool SetSelected)
{
	if (ListItem* pItem = GetItem(Row, Column))
	{
		if (SetSelected != pItem->GetParent()->IsSelected())
		{
			if (SetSelected && !(m_Properties & LP_MULTISELECT))
				ClearAllSelectedItems();

			if (SetSelected)
			{
				pItem->GetParent()->m_SelectedIdx = UTIL_HIGH(0, Column);
			} else
			{
				pItem->GetParent()->m_SelectedIdx =  -1;
			} 
		}
	}
}

int UIListWidget::GetAllSelectedItems(std::vector<ListItem*>& Set)
{
	for (int i = 0; i < (int)m_ListItems.size(); i++)
	{
		ListData* pData = m_ListItems[i];
		if (pData->m_IsColumnHeader) continue;
		if (pData->IsSelected())
		{
			Set.push_back(pData);
		}
	}
	return (int)Set.size();
}

int UIListWidget::GetAllSelectedItems(std::vector<int>& Set)
{
	for (int i = 0; i < (int)m_ListItems.size(); i++)
	{
		ListData* pData = m_ListItems[i];
		if (pData->m_IsColumnHeader) continue;
		if (pData->IsSelected())
		{
			if (m_Properties & LP_COLUMNHEADERS)
				Set.push_back(i-1);
			else
				Set.push_back(i);
		}
	}
	return (int)Set.size();
}

void UIListWidget::ClearAllSelectedItems()
{
	for (int i = 0; i < (int)m_ListItems.size(); i++)
	{
		m_ListItems[i]->m_SelectedIdx = -1;
	}
}

int UIListWidget::InsertItem(const std::string& Text, int Row)
{
	return InsertItem( lang::Lang::ConvertAtoW(Text), "", math::Vec2(0,0), Row );
}

int UIListWidget::InsertItem(const std::wstring& Text, int Row)
{
	return InsertItem(Text, "", math::Vec2(0,0), Row);
}

int UIListWidget::InsertItem(const std::wstring& Text, const std::string& Icon, const math::Vec2& IconSize, int Row)
{
	bool HasColumnHeaders = IsTable() && (m_Properties & LP_COLUMNHEADERS);
	int idx;
	ListData* pData = new ListData();
	pData->m_Text = Text;
	pData->m_Icon = Icon;
	pData->m_TextColor = m_DataColor;
	InitTextObj(pData, 0);

	if (IsTable())
	{
		// insert empty column fields
		for (unsigned int i = 1; i < m_Width; i++)
		{
			ListItem item(pData);
			item.m_TextColor = m_DataColor;
			InitTextObj(&item, i);
			pData->m_ColumnData.push_back(item);
		}
	}

	if (Row < 0)
	{
		if (m_pSortFunc && (m_Properties & LP_CANSORT))
		{
			int Start = HasColumnHeaders ? 1 : 0;
			idx = -1;
			for (int i = Start; i < (int)m_ListItems.size(); i++)
			{
				if (m_pSortFunc(pData, m_ListItems[i], this, m_pvSortCBData) < 0)
				{
					m_ListItems.insert(m_ListItems.begin() + i, pData);
					idx = i;
					break;
				}
			}
			if (idx == -1)
			{
				m_ListItems.push_back(pData);
				idx = (int)m_ListItems.size() - 1;
			}
		}
		else if (HasColumnHeaders)
		{
			if (m_ListItems.size() > 1)
			{
				m_ListItems.insert(m_ListItems.begin() + 1, pData);
				idx = 1;
			}
			else
			{
				m_ListItems.push_back(pData);
				idx = (int)m_ListItems.size() - 1;
			}
		}
		else
		{
			m_ListItems.insert(m_ListItems.begin(), pData);
			idx = 0;
		}
	}
	else if (Row < (int)m_ListItems.size())
	{
		if (HasColumnHeaders)
		{
			Row++;
			if (Row < (int)m_ListItems.size())
			{
				m_ListItems.insert(m_ListItems.begin() + Row, pData);
				idx = Row;
			}
			else
			{
				m_ListItems.push_back(pData);
				idx = (int)m_ListItems.size() - 1;
			}
		}
		else
		{
			m_ListItems.insert(m_ListItems.begin() + Row, pData);
			idx = Row;
		}
	}
	else
	{
		m_ListItems.push_back(pData);
		idx = (int)m_ListItems.size() - 1;
	}

	if (HasColumnHeaders)
		idx--;

	if (!Text.empty())	SetItemTextW(Text, idx);
	if (!Icon.empty())	SetItemIcon(Icon, IconSize, idx);

	m_SliderCacheV = -1.0f;
	m_CacheDirty = true;
	return idx;
}

void UIListWidget::DeleteItem(int Row)
{
	if (IsTable() && (m_Properties & LP_COLUMNHEADERS))
		Row++;

	if (Row < (int)m_ListItems.size())
	{
		if (m_pHover == m_ListItems[Row])
			m_pHover = NULL;

		delete m_ListItems[Row];
		m_ListItems.erase(m_ListItems.begin() + Row);

		m_SliderCacheV = -1.0f;
		m_CacheDirty = true;
	}
}

void UIListWidget::DeleteAllItems()
{
	if (m_ListItems.empty()) return; // already empty

	for (int i = (int)(m_ListItems.size() - 1); i >= 0; i--)
	{
		ListData* pData = m_ListItems[i];
		if ((i == 0) && pData->m_IsColumnHeader) break;

		delete pData;
		m_ListItems.erase(m_ListItems.begin() + i);
	}

	m_SliderCacheV = -1.0f;
	m_CacheDirty = true;
}

bool UIListWidget::AddColumnA(const std::string& Text)
{
	return AddColumnW( lang::Lang::ConvertAtoW(Text) );
}

bool UIListWidget::AddColumnW(const std::wstring& Text)
{
	if (IsGrid()) return false;

	COLUMN_SPECIFICS col;
	m_ColumnSpecs.push_back(col);

	for (int i = 0; i < (int)m_ListItems.size(); i++)
	{
		// insert blank columns
		ListItem item(m_ListItems[i]);
		item.m_TextColor = m_DataColor;
		InitTextObj(&item, m_Width);
		m_ListItems[i]->m_ColumnData.push_back(item);
	}

	if ((m_Properties & LP_COLUMNHEADERS) && !m_ListItems.empty() && !Text.empty())
	{
		ListData* pData = m_ListItems[0];
		if (pData->m_IsColumnHeader)
			SetColumnTextW(Text, (int)m_ListItems[0]->m_ColumnData.size());
	}

	m_Width++;
	m_SliderCacheH = -1.0f;
	m_CacheDirty = true;
	return true;
}

std::string UIListWidget::GetColumnTextA(int Column)
{
	return lang::Lang::ConvertWtoA( GetColumnTextW(Column) );
}

std::wstring UIListWidget::GetColumnTextW(int Column)
{
	if (IsGrid()) return L"";

	if ((m_Properties & LP_COLUMNHEADERS) && !m_ListItems.empty())
	{
		ListData* pData = m_ListItems[0];
		if (pData->m_IsColumnHeader)
		{
			if (Column > 0)
			{
				if (Column <= (int)pData->m_ColumnData.size())
					return pData->m_ColumnData[Column-1].m_Text;
			}
			else
			{
				return pData->m_Text;
			}
		}
	}

	return L"";
}

void UIListWidget::SetColumnTextA(const std::string& Text, int Column)
{
	SetColumnTextW( lang::Lang::ConvertAtoW(Text), Column );
}

void UIListWidget::SetColumnTextW(const std::wstring& Text, int Column)
{
	if ((m_Properties & LP_COLUMNHEADERS) && !m_ListItems.empty())
	{
		ListData* pData = m_ListItems[0];
		if (pData->m_IsColumnHeader)
		{
			float Height = (m_Properties & LP_VARIABLE_HEIGHT) ? pData->m_Height : m_RowHeight;
			if (Column > 0)
			{
				if (Column <= (int)pData->m_ColumnData.size())
				{
					ListItem& item = pData->m_ColumnData[Column-1];
					item.m_Text = Text;
					item.m_TextObj.SetOwner(this);
					if ((item.m_Width > 0.0f) && (Height > 0.0f))
						item.m_TextObj.SetExtents(item.m_TextObj.GetPos(), math::Vec2(item.m_Width, Height));
					else
					{
						item.m_TextObj.SetExtents(math::Vec2(0.0f, 0.0f), math::Vec2(1.0f, 1.0f));
						m_CacheDirty = true;
					}
					item.m_TextObj.SetText(Text);
				}
			}
			else
			{
				pData->m_Text = Text;
				pData->m_TextObj.SetOwner(this);
				if ((pData->m_Width > 0.0f) && (Height > 0.0f))
					pData->m_TextObj.SetExtents(pData->m_TextObj.GetPos(), math::Vec2(pData->m_Width, Height));
				else
				{
					pData->m_TextObj.SetExtents(math::Vec2(0.0f, 0.0f), math::Vec2(1.0f, 1.0f));
					m_CacheDirty = true;
				}
				pData->m_TextObj.SetText(Text);
			}
		}
	}
}

int UIListWidget::GetItemC()
{
	int Count = (int)m_ListItems.size();
	if (IsTable() && (m_Properties & LP_COLUMNHEADERS))
		Count--;
	return Count;
}

int UIListWidget::GetRowC()
{
	RecreateList();
	if (IsTable() && (m_Properties & LP_COLUMNHEADERS))
		return m_Height - 1;
	else
		return m_Height;
}

int UIListWidget::GetColumnC()
{
	RecreateList();
	return m_Width;
}
int UIListWidget::GetMaxVisibleRows(int StartRow)
{
	RecreateList();
	if (m_Properties & LP_VARIABLE_HEIGHT)
	{
		int RowC = 0;
		float TableH = GetSize().y;
		for (int i = StartRow; i < (int)m_ListItems.size(); i++)
		{
			ListData* pData = NULL;
			if ((m_Properties & LP_COLUMNHEADERS) && (StartRow > 0))
			{
				if (i == StartRow) // first row is always column header
					pData = m_ListItems[0];
				else
					pData = m_ListItems[i-1];
			}
			else
				pData = m_ListItems[i];

			TableH -= (pData->m_Height + m_RowSpacing);
			if (TableH < 0.0f)
				break;
			
			RowC++;
		}
		return RowC;
	}
	else if (m_RowHeight > 0.0f)
		return (int)(GetSize().y / (m_RowHeight + m_RowSpacing));
	else
		return 0;
}

int UIListWidget::GetMaxVisibleCols(int StartCol)
{
	float MaxW = GetSize().x;
	float CurW = 0.0f;
	int ColC = 0;

	RecreateList();

	for (int i = StartCol; i < (int)m_ColumnSpecs.size(); i++)
	{
		COLUMN_SPECIFICS& col = m_ColumnSpecs[i];
		CurW += m_ColumnSpacing + col.Width;
		if (CurW > MaxW)
			break;
		ColC++;
	}

	return ColC;
}

int UIListWidget::GetHScroll()
{
	RecreateList();
	return m_CurrentCol;
}

void UIListWidget::SetHScroll(int Val, bool bAbsolute)
{
	if (IsGrid()) return;
	if (!(m_Properties & LP_CANHSCROLL)) return;

	if (bAbsolute)
		m_CurrentCol = Val;
	else
		m_CurrentCol += Val;

	if (m_CurrentCol < 0)
		m_CurrentCol = 0;
}

int UIListWidget::GetVScroll()
{
	RecreateList();
	return m_CurrentRow;
}

void UIListWidget::SetVScroll(int Val, bool bAbsolute)
{
	if (!(m_Properties & LP_CANVSCROLL)) return;

	if (bAbsolute)
		m_CurrentRow = Val;
	else
		m_CurrentRow += Val;

	if (m_CurrentRow < 0)
		m_CurrentRow = 0;
}

int UIListWidget::SetVScrollToEnd()
{
	if (!(m_Properties & LP_CANVSCROLL)) return 0;
	if (m_ListItems.empty()) return 0;

	RecreateList();

	int RowC = (m_Properties & LP_COLUMNHEADERS) ? 2 : 1;
	int MaxRowC;

	m_CurrentRow = (int)(m_ListItems.size() - 1);

	while (m_CurrentRow > 0)
	{
		MaxRowC = GetMaxVisibleRows(m_CurrentRow);
		if (MaxRowC < RowC)
		{
			m_CurrentRow++;
			break;
		}
		m_CurrentRow--;
		RowC++;
	}

	return m_CurrentRow;
}

void UIListWidget::ClearList()
{
	for (int i = 0; i < (int)m_ListItems.size(); i++)
		delete m_ListItems[i];

	m_ListItems.clear();
	m_Width = 1;
	m_Height = 0;
	m_CurrentRow = m_CurrentCol = 0;
	m_CacheDirty = true;
}

void UIListWidget::ReSortList(bool ForceRefresh)
{
	if (!m_pSortFunc || !(m_Properties & LP_CANSORT)) return; // cannot sort
	if (m_ListItems.size() < 2) return; // nothing to sort

	std::vector<ListData*> TempList;
	bool bHasColumnHeaders = m_ListItems[0]->m_IsColumnHeader;

	if (bHasColumnHeaders && (m_ListItems.size() < 3)) return; // nothing to sort

	// copy elements to temp list
	m_ListItems.swap(TempList);

	// insert column headers first
	if (bHasColumnHeaders)
	{
		m_ListItems.push_back(TempList[0]);
		TempList.erase(TempList.begin());
	}

	// insert each item from temp list back into actual list
	for (int i = 0; i < (int)TempList.size(); i++)
	{
		ListData* pData = TempList[i];

		// first item automatically gets pushed in
		if (m_ListItems.empty() || (bHasColumnHeaders && (m_ListItems.size() == 1)))
			m_ListItems.push_back(pData);
		else
		{
			bool bAdded = false;
			for (int j = bHasColumnHeaders ? 1 : 0; j < (int)m_ListItems.size(); j++)
			{
				if (m_pSortFunc(pData, m_ListItems[j], this, m_pvSortCBData) < 0)
				{
					m_ListItems.insert(m_ListItems.begin() + j, pData);
					bAdded = true;
					break;
				}
			}
			if (!bAdded)
				m_ListItems.push_back(pData);
		}
	}

	TempList.clear();
	m_CacheDirty = m_CacheDirty || ForceRefresh;
}

void UIListWidget::SetListProperty(LIST_PROPERTIES Property)
{
	if (m_Properties & Property) return; // already set
	m_Properties |= Property;
	RecreateList(true); // always force a refresh after a property change
}

void UIListWidget::RemoveListProperty(LIST_PROPERTIES Property)
{
	if (!(m_Properties & Property)) return; // already set
	m_Properties &= ~Property;
	RecreateList(true); // always force a refresh after a property change
}

bool UIListWidget::GetListProperty(LIST_PROPERTIES Property)
{
	return (m_Properties & Property) ? true : false;
}

void UIListWidget::ResetListProperty(unsigned long Properties)
{
	m_Properties = Properties;
	RecreateList(true); // always force a refresh after property change
}

void UIListWidget::SetRowHeight(float f)
{
	if (!(m_Properties & LP_MANUALROWSIZE)) return;
	if (m_Properties & LP_VARIABLE_HEIGHT) return;
	m_RowHeight = f;
}

float UIListWidget::GetRowHeight(int Row, bool AllowGetHeader)
{
	RecreateList();

	if (m_Properties & LP_VARIABLE_HEIGHT)
	{
		if (ListItem* pItem = GetItem(Row, -1, AllowGetHeader))
			return pItem->GetParent()->m_Height;
		return 0.0f;
	}
	else
		return m_RowHeight;
}

void UIListWidget::SetColumnWidth(float f, int Col)
{
	if (!(m_Properties & LP_MANUALCOLUMNSIZE)) return;
	/*
	for (int i = 0; i < (int)m_ListItems.size(); i++)
	{
		ListData* pData = m_ListItems[i];

		if (IsTable() && (Col > 0))
		{
			if (Col <= (int)pData->m_ColumnData.size())
			{
				pData->m_ColumnData[Col-1].m_Width = f;
			}
		}
		else
		{
			pData->m_Width = f;
		}
	}
	*/
	if (Col < (int)m_ColumnSpecs.size())
		m_ColumnSpecs[Col].Width = f;

	m_CacheDirty = true;
}

float UIListWidget::GetColumnWidth(int Col)
{
	if (m_Properties & LP_MANUALCOLUMNSIZE)
	{
		if (Col < (int)m_ColumnSpecs.size())
			return m_ColumnSpecs[Col].Width;
	}
	else
	{
		RecreateList();
		if (m_ListItems.empty()) return 0.0f;

		ListData* pData = m_ListItems[0];

		if (IsTable() && (Col > 0))
		{
			if (Col <= (int)pData->m_ColumnData.size())
				return pData->m_ColumnData[Col-1].m_Width;
			else
				return 0.0f;
		}
		else
			return pData->m_Width;
	}
	return 0.0f;
}

bool UIListWidget::ExportSpecificsToFile(script::WriteLua*	pWL)
{
	const float Inv255f = 1.0f / 255.0f;
	math::Vec4 Color;

	RecreateList();

	pWL->WritePair("ListType", MapListToString(m_ListType));
	pWL->WritePair("RowSpacing", m_RowSpacing);
	pWL->WritePair("ColumnSpacing", m_ColumnSpacing);
	pWL->WritePair("DataFont", m_DataFont);

	pWL->WritePair("DataColor", math::Vec4( Inv255f * (float)ColorGetR(m_DataColor),
											Inv255f * (float)ColorGetG(m_DataColor),
											Inv255f * (float)ColorGetB(m_DataColor),
											Inv255f * (float)ColorGetA(m_DataColor) ));

	pWL->WritePair("CanScrollVert", (int)((m_Properties & LP_CANVSCROLL) ? 1 : 0));
	pWL->WritePair("CanSort", (int)((m_Properties & LP_CANSORT) ? 1 : 0));
	pWL->WritePair("AutoRowHeight", (int)((m_Properties & LP_MANUALROWSIZE) ? 0 : 1));
	pWL->WritePair("EnableMultiSelect", (int)((m_Properties & LP_MULTISELECT) ? 1 : 0));

	if (IsTable())
	{
		pWL->WritePair("ColumnFont", m_ColumnFont);

		pWL->WritePair("ColumnColor", math::Vec4( Inv255f * (float)ColorGetR(m_ColumnColor),
												  Inv255f * (float)ColorGetG(m_ColumnColor),
												  Inv255f * (float)ColorGetB(m_ColumnColor),
												  Inv255f * (float)ColorGetA(m_ColumnColor) ));

		pWL->WritePair("ColumnHeaders", (int)((m_Properties & LP_COLUMNHEADERS) ? 1 : 0));
		pWL->WritePair("CanScrollHorz", (int)((m_Properties & LP_CANHSCROLL) ? 1 : 0));
		pWL->WritePair("AutoColumnWidth", (int)((m_Properties & LP_MANUALCOLUMNSIZE) ? 0 : 1));
		pWL->WritePair("VariableHeight", (int)((m_Properties & LP_VARIABLE_HEIGHT) ? 0 : 1));

		pWL->WritePair("ColumnC", GetColumnC());
	}
	else
	{
		pWL->WritePair("DisableText", (int)((m_Properties & LP_DISABLETEXT) ? 1 : 0));
	}

	if (m_Properties & LP_CANVSCROLL)
	{
		if (m_pSliderV)
			pWL->WritePair("VertScrollBar", m_pSliderV->GetName());
		else
			pWL->WritePair("VertScrollBar", m_DelayedLoad.LoadVSlider);
	}

	if (m_Properties & LP_CANHSCROLL)
	{
		if (m_pSliderH)
			pWL->WritePair("HorzScrollBar", m_pSliderH->GetName());
		else
			pWL->WritePair("HorzScrollBar", m_DelayedLoad.LoadHSlider);
	}

	if (m_Properties & LP_MANUALROWSIZE)
		pWL->WritePair("RowHeight", m_RowHeight);

	if (IsTable())
	{
		for (int i = 0; i < (int)m_ColumnSpecs.size(); i++)
		{
			if (m_Properties & LP_COLUMNHEADERS)
				pWL->WritePair("ColumnTitle" + lang::Lang::FormatNumber(i+1), GetColumnTextA(i));

			if (m_Properties & LP_MANUALCOLUMNSIZE)
				pWL->WritePair("ColumnWidth" + lang::Lang::FormatNumber(i+1), m_ColumnSpecs[i].Width);

			pWL->WritePair("ColumnAlignH" + lang::Lang::FormatNumber(i+1), UIText::MapHorizJustToString(m_ColumnSpecs[i].DefaultHJust));
			pWL->WritePair("ColumnAlignV" + lang::Lang::FormatNumber(i+1), UIText::MapVertJustToString(m_ColumnSpecs[i].DefaultVJust));
		}
	}

	return true;
}

void UIListWidget::RecreateList(bool bForce)
{
	if (!bForce && !m_CacheDirty) return;
	m_CacheDirty = false;

	/**************************************************************
	// recalculate cached data and verify existing counters/indices
	*/

	// check column headers
	if (IsTable() && m_Properties & LP_COLUMNHEADERS)
	{
		if (m_ListItems.empty() || !m_ListItems[0]->m_IsColumnHeader)
		{
			// create blank column headers
			ListData* pData = new ListData();
			pData->m_IsColumnHeader = true;
			pData->m_TextColor = m_ColumnColor;
			InitTextObj(pData, 0);
			for (int i = 1; i < (int)m_Width; i++)
			{
				ListItem item(pData);
				item.m_TextColor = m_ColumnColor;
				InitTextObj(&item, i);
				pData->m_ColumnData.push_back(item);
			}
			m_ListItems.insert(m_ListItems.begin(), pData);
		}
	}
	else
	{
		if (!m_ListItems.empty() && m_ListItems[0]->m_IsColumnHeader)
		{
			delete m_ListItems[0];
			m_ListItems.erase(m_ListItems.begin());
		}
	}

	// collect maximum cell dimensions data
	if (!(m_Properties & LP_MANUALROWSIZE))
		m_RowHeight = 0.0f;

	if (!(m_Properties & LP_MANUALCOLUMNSIZE))
	{
		for (int i = 0; i < (int)m_ColumnSpecs.size(); i++)
			m_ColumnSpecs[i].Width = 0.0f;
	}

	for (int i = 0; i < (int)m_ListItems.size(); i++)
	{
		ListData* pData = m_ListItems[i];

		if (m_Properties & LP_VARIABLE_HEIGHT)
		{
			pData->m_Height = 0.0f;
			CalcCellDim(pData, m_ColumnSpecs[0].Width, pData->m_Height);
		}
		else
			CalcCellDim(pData, m_ColumnSpecs[0].Width, m_RowHeight);

		if (IsTable())
		{
			for (int j = 0; j < (int)pData->m_ColumnData.size(); j++)
			{
				ListItem& item = pData->m_ColumnData[j];

				if (m_Properties & LP_VARIABLE_HEIGHT)
					CalcCellDim(&item, m_ColumnSpecs[j+1].Width, pData->m_Height);
				else
					CalcCellDim(&item, m_ColumnSpecs[j+1].Width, m_RowHeight);
			}
		}
	}

	// if total column width does not exceed list widget's width, normalize column widths
	if (!(m_Properties & LP_MANUALCOLUMNSIZE))
	{
		float Total = 0.0f;
		for (int i = 0; i < (int)m_ColumnSpecs.size(); i++)
		{
			Total += m_ColumnSpecs[i].Width;
			if (i > 0)
				Total += m_ColumnSpacing;
		}

		if ((Total > 0.0f) && (Total < GetSize().x))
		{
			float factor = GetSize().x / Total;
			for (int i = 0; i < (int)m_ColumnSpecs.size(); i++)
				m_ColumnSpecs[i].Width *= factor;
		}
	}

	// apply cell dimensions data
	for (int i = 0; i < (int)m_ListItems.size(); i++)
	{
		ListData* pData = m_ListItems[i];
		if (IsTable())
		{
			pData->m_Width = m_ColumnSpecs[0].Width;
			for (int j = 0; j < (int)pData->m_ColumnData.size(); j++)
			{
				ListItem& item = pData->m_ColumnData[j];
				item.m_Width = m_ColumnSpecs[j+1].Width;
			}
		}
		else
		{
			// grid should always be uniform
			pData->m_Width = m_RowHeight * GraphicsUtil::H2W;
		}
	}

	// recalculate overall width and height of list
	if (IsGrid())
	{
		float AvailableW = GetSize().x - (m_RowHeight * GraphicsUtil::H2W) - m_ColumnSpacing;
		m_Width = 1;
		while (AvailableW > 0.0f)
		{
			m_Width++;
			AvailableW -= (m_RowHeight * GraphicsUtil::H2W) + m_ColumnSpacing;
		}

		m_Height = (unsigned int)ceil((float)m_ListItems.size() / (float)m_Width);
	}
	else
	{
		// width doesn't change, is always equal to columns
		m_Height = (int)m_ListItems.size();
		if (m_Properties & LP_COLUMNHEADERS)
			m_Height--;
	}

	UTIL_CLAMP(m_CurrentRow, 0, UTIL_HIGH((int)0, (int)m_Height - 1));
	UTIL_CLAMP(m_CurrentCol, 0, UTIL_HIGH((int)0, (int)m_Width - 1));
}

void UIListWidget::CalcCellDim(ListItem* pItem, float& MaxW, float& MaxH)
{
	bool DontCalcH = ((MaxH > 0.0f) && (m_Properties & LP_MANUALROWSIZE));
	bool DontCalcW = ((MaxW > 0.0f) && (m_Properties & LP_MANUALCOLUMNSIZE));

	if (DontCalcH && DontCalcW) return;

	float width = 0.0f;
	float height = 0.0f;

	if (pItem->m_Sprite.IsValid())
	{
		width = pItem->m_Sprite.m_Size.x;
		height = pItem->m_Sprite.m_Size.y;
	}

	if (!pItem->m_Text.empty() && (IsTable() || !(m_Properties & LP_DISABLETEXT)))
	{
		float FontH = 0.0f;

		if (Font* pFont = pItem->m_TextObj.GetFont())
			FontH = pFont->GetHeight();

		pItem->m_TextObj.SetExtents(math::Vec2(0.0f, 0.0f), math::Vec2(DontCalcW ? MaxW : GetSize().x, DontCalcH ? MaxH : GetSize().y));
		pItem->m_TextObj.RefreshText();
		width = UTIL_HIGH(width, pItem->m_TextObj.GetSize().x + (2.0f*pItem->m_TextObj.GetBorder().x));
		height += UTIL_HIGH(FontH, pItem->m_TextObj.GetSize().y + (2.0f*pItem->m_TextObj.GetBorder().y));
	}

	if (!DontCalcW)	MaxW = UTIL_HIGH(MaxW, width);
	if (!DontCalcH)	MaxH = UTIL_HIGH(MaxH, height);
}

void UIListWidget::RenderItem(ListItem* pItem, const math::Vec2& Pos, const math::Vec2& Size, const int row, const int col)
{
	Font* pFont = NULL;
	float FontH = 0.0f;
	UIText::TEXT_JUST_HORIZ HJust = m_ColumnSpecs[col].DefaultHJust;
	UIText::TEXT_JUST_VERT VJust = m_ColumnSpecs[col].DefaultVJust;

	// gather alignment specifics
	if (!pItem->m_Text.empty() && (IsTable() || !(m_Properties & LP_DISABLETEXT)))
	{
		if (pFont = pItem->m_TextObj.GetFont())
			FontH = pFont->GetHeight();
	}

	// render sprite
	if (pItem->m_Sprite.IsValid())
	{
		if (UIText::TJH_LEFT == HJust)
			pItem->m_Sprite.m_Pos.x = Pos.x;
		else if (UIText::TJH_RIGHT == HJust)
			pItem->m_Sprite.m_Pos.x = Pos.x + Size.x - pItem->m_Sprite.m_Size.x;
		else
			pItem->m_Sprite.m_Pos.x = Pos.x + (0.5f * (Size.x - pItem->m_Sprite.m_Size.x));

		if (UIText::TJV_TOP == VJust)
			pItem->m_Sprite.m_Pos.y = Pos.y;
		else if (UIText::TJV_BOTTOM == VJust)
			pItem->m_Sprite.m_Pos.y = Pos.y + Size.y - pItem->m_Sprite.m_Size.y - FontH;
		else
			pItem->m_Sprite.m_Pos.y = Pos.y + (0.5f * (Size.y - pItem->m_Sprite.m_Size.y - FontH));

		pItem->m_Sprite.Render();
	}

	// render text
	if (pFont)
	{
		pItem->m_TextObj.SetColor(pItem->m_TextColor);
		if (pItem->m_Sprite.IsValid())
		{
			// render text below icon
			pItem->m_TextObj.SetExtents(math::Vec2(Pos.x, Pos.y + pItem->m_Sprite.m_Size.y), math::Vec2(Size.x, FontH));
			pItem->m_TextObj.Render();
		}
		else
		{
			pItem->m_TextObj.SetExtents(Pos, Size);
			pItem->m_TextObj.Render();
		}
	}
}

void UIListWidget::InitTextObj(ListItem* pItem, int Col)
{
	pItem->m_TextObj.SetFont( pItem->GetParent()->m_IsColumnHeader ? m_ColumnFont : m_DataFont );

	if (pItem->m_CustomHJust >= 0)
		pItem->m_TextObj.SetHorizJust( (UIText::TEXT_JUST_HORIZ)pItem->m_CustomHJust );
	else
		pItem->m_TextObj.SetHorizJust( m_ColumnSpecs[Col].DefaultHJust );

	if (pItem->m_CustomVJust >= 0)
		pItem->m_TextObj.SetVertJust( (UIText::TEXT_JUST_VERT)pItem->m_CustomVJust );
	else
		pItem->m_TextObj.SetVertJust( m_ColumnSpecs[Col].DefaultVJust );
}

float UIListWidget::CalcTotalRowH(int From, int To)
{
	float RowH = 0.0f;
	RecreateList();

	if (m_Properties & LP_VARIABLE_HEIGHT)
	{
		for (int i = From; i < (int)m_ListItems.size(); i++)
		{
			if ((To >= 0) && (i > To)) break;

			if ((m_Properties & LP_COLUMNHEADERS) && (From > 0))
			{
				if (i == From) // first row is always column header
					RowH += m_ListItems[0]->m_Height;
				else
					RowH += m_ListItems[i-1]->m_Height;
			}
			else
				RowH += m_ListItems[i]->m_Height;
			if (i > 0)
				RowH += m_RowSpacing;
		}
	}
	else
	{
		int ItemC = (To >= 0) ? (To - From + 1) : (int)m_ListItems.size();
		RowH += m_RowHeight * (float)ItemC;
		RowH += m_RowSpacing * (float)UTIL_HIGH(0, ItemC-1);
	}

	return RowH;
}

float UIListWidget::CalcTotalColumnW(int From, int To)
{
	float ColW = 0.0f;
	RecreateList();

	for (int i = From; i < (int)m_ColumnSpecs.size(); i++)
	{
		if ((To >= 0) && (i > To)) break;

		ColW += m_ColumnSpecs[i].Width;
		if (i > 0)
			ColW += m_ColumnSpacing;
	}

	return ColW;
}

int UIListWidget::DefaultSortFuncCB(ListItem* pItem1, ListItem* pItem2, UIListWidget* pWidget, void* pvData)
{
	// just do a regular strcmp for alphabetical order sorting
	return wcscmp(pItem1->m_Text.c_str(), pItem2->m_Text.c_str());
}



// static functions

std::string UIListWidget::MapListToString(LIST_TYPE t)
{
	switch (t)
	{
	case LT_GRID:		return "grid";
	case LT_TABLE:
	default:			return "table";
	}
}

UIListWidget::LIST_TYPE UIListWidget::MapStringToList(const std::string& t)
{
	for (LIST_TYPE i = (LIST_TYPE)0; i < LT_COUNT; i = (LIST_TYPE)(i+1))
	{
		if (stricmp(t.c_str(), MapListToString(i).c_str()) == 0)
			return i;
	}
	return LT_TABLE;
}



void UIListWidget::SetAlignmentStr(const std::string& HJust, const std::string& VJust, int Column)
{
	SetAlignment( UIText::MapStringToHorizJust(HJust),
				  UIText::MapStringToVertJust(VJust),
				  Column );
}

void UIListWidget::SetCellAlignStr(const std::string& HJust, const std::string& VJust, int Row, int Column)
{
	SetCellAlignment( UIText::MapStringToHorizJust(HJust),
					  UIText::MapStringToVertJust(VJust),
					  Row, Column );
}

int UIListWidget::InsertItemStr(const std::string& Text, int Row)
{
	return InsertItem(Text, Row);
}

// luabind

void UIListWidget::Register(LuaPlus::LuaState* _pScript)
{
#ifdef _ENABLE_LUABIND
	// TODO: register script functions
	lua_State * L = _pScript->GetCState ( );

	luabind::module(L)
    [
    luabind::class_<UIListWidget, UIWidget >( "ListWidget" )
		.def( "SetColumnFont",			&UIListWidget::SetColumnFont)
		.def( "SetDataFont",			&UIListWidget::SetDataFont)
		.def( "SetAlignment",			&UIListWidget::SetAlignmentStr)
		.def( "SetCellAlignment",		&UIListWidget::SetCellAlignStr)
		.def( "SetItemText",			&UIListWidget::SetItemTextA)
		.def( "GetItemText",			&UIListWidget::GetItemTextA)
		.def( "SetItemIcon",			&UIListWidget::SetItemIcon)
		.def( "GetItemIcon",			&UIListWidget::GetItemIcon)
		.def( "SetItemData",			&UIListWidget::SetItemData)
		.def( "GetItemData",			&UIListWidget::GetItemData)
		.def( "ClearAllSelectedItems",	&UIListWidget::ClearAllSelectedItems)
		.def( "InsertItem",				&UIListWidget::InsertItemStr)
		.def( "DeleteItem",				&UIListWidget::DeleteItem)
		.def( "DeleteAllItems",			&UIListWidget::DeleteAllItems)
		.def( "AddColumn",				&UIListWidget::AddColumnA)
		.def( "GetColumnText",			&UIListWidget::GetColumnTextA)
		.def( "SetColumnText",			&UIListWidget::SetColumnTextA)
		.def( "GetItemC",				&UIListWidget::GetItemC)
		.def( "GetRowC",				&UIListWidget::GetRowC)
		.def( "GetColumnC",				&UIListWidget::GetColumnC)
		.def( "GetMaxVisibleRows",		&UIListWidget::GetMaxVisibleRows)
		.def( "GetMaxVisibleCols",		&UIListWidget::GetMaxVisibleCols)
		.def( "SetHScroll",				&UIListWidget::SetHScroll)
		.def( "SetVScroll",				&UIListWidget::SetVScroll)
		.def( "ClearList",				&UIListWidget::ClearList)
		.def( "ReSortList",				&UIListWidget::ReSortList)
		.def( "SetRowHeight",			&UIListWidget::SetRowHeight)
		.def( "GetRowHeight",			&UIListWidget::GetRowHeight)
		.def( "SetColumnWidth",			&UIListWidget::SetColumnWidth)
		.def( "GetColumnWidth",			&UIListWidget::GetColumnWidth)
		.def( "SetRowSpacing",			&UIListWidget::SetRowSpacing)
		.def( "GetRowSpacing",			&UIListWidget::GetRowSpacing)
		.def( "SetColumnSpacing",		&UIListWidget::SetColumnSpacing)
		.def( "GetColumnSpacing",		&UIListWidget::GetColumnSpacing)
		.def( "ResetSliders",			&UIListWidget::ResetSliders)
	];
#endif // _ENABLE_LUABIND    
	
}
